package com.example.selfservicestation.util

import android.content.Context
import android.media.MediaPlayer
import android.media.SoundPool
import com.example.selfservicestation.R
import java.io.File
import java.io.RandomAccessFile
import java.util.concurrent.ConcurrentHashMap

/**
 * 音频播放器
 */
object Audio {

    // 得有一个引用指向播放中的播放器，否则播放器可能会在播放中被 GC，造成声音中断
    private var player: MediaPlayer? = null
    private val pendings = ArrayList<Player>()
    private val playedTimes = ConcurrentHashMap<String, Long>()
    private const val MIN_PLAY_INTERVAL_SECONDS = 2

    class Player(
        val resource: String,
        val player: MediaPlayer,
        val callback: (() -> Unit)? = null
    )

    fun play(ctx: Context, resId: Int, callback: (() -> Unit)? = null) {
        play(Player(resId.toString(), MediaPlayer.create(ctx, resId), callback))
    }

    fun play(file: File, callback: (() -> Unit)? = null) {
        val player = MediaPlayer()
        val raf = RandomAccessFile(file, "r")
        player.setDataSource(raf.fd, 0, raf.length())
        raf.close()
        player.prepare()
        play(Player(file.absolutePath, player, callback))
    }

    /**
     * 注意：如果加入队列的 MediaPlayer 不能及时播放，则会让 start 方法失效，再播放是会触发 -19 错误
     * 因此，使用者应控制该接口的调用频率
     */
    private fun play(player: Player) {
        // 频率控制
        playedTimes[player.resource]?.let {
            val lastPlayedTime = it
            AudioFrequencyConfig.CONFIGS[player.resource].let { interval ->
                val now = jvmSeconds()
                if (now - lastPlayedTime < (interval ?: MIN_PLAY_INTERVAL_SECONDS)) {
                    return
                }
            }
        }

        synchronized(pendings) {
            if (this.player != null) {
                pendings.add(player)
                return
            }
            this.player = player.player
        }
        player.player.setOnErrorListener { _, what, extra ->
            Logger.e("Audio", "语音出错 what: ${what}, extra: $extra")
            return@setOnErrorListener false
        }
        player.player.setOnCompletionListener {
            synchronized(pendings) {
                this.player?.let { player ->
                    if (player == it) {
                        player.release()
                        this.player = null
                    }
                }
                if (pendings.isNotEmpty()) {
                    play(pendings.removeAt(0))
                }
                player.callback?.let { it() }
            }
            Logger.d("Audio", "Audio Completion")
        }
        player.player.start()

        val now = jvmSeconds()
        playedTimes[player.resource] = now

        Logger.d("Audio", "Audio Starting")
    }

    fun stopAll() {
        synchronized(pendings) {
            pendings.clear()
            player?.stop()
            player = null
        }
    }

    private val soundPool = SoundPool.Builder().setMaxStreams(2).build()

    fun initSoundPool(ctx: Context) {
        soundPool.load(ctx, R.raw.effect_click_6, 1)
        soundPool.load(ctx, R.raw.effect_click_3, 1)

        soundPool.load(ctx, R.raw.n0, 1)
        soundPool.load(ctx, R.raw.n1, 1)
        soundPool.load(ctx, R.raw.n2, 1)
        soundPool.load(ctx, R.raw.n3, 1)
        soundPool.load(ctx, R.raw.n4, 1)
        soundPool.load(ctx, R.raw.n5, 1)
        soundPool.load(ctx, R.raw.n6, 1)
        soundPool.load(ctx, R.raw.n7, 1)
        soundPool.load(ctx, R.raw.n8, 1)
        soundPool.load(ctx, R.raw.n9, 1)
        soundPool.load(ctx, R.raw.n10, 1)
        soundPool.load(ctx, R.raw.n100, 1)
        soundPool.load(ctx, R.raw.n1000, 1)
        soundPool.load(ctx, R.raw.n10000, 1)
        soundPool.load(ctx, R.raw.ndot, 1)
        soundPool.load(ctx, R.raw.point, 1)
        soundPool.load(ctx, R.raw.kg, 1)
        soundPool.load(ctx, R.raw.unit_of_bottle, 1)
    }

    const val DEFAULT_EFFECT = 1
    const val SPECIAL_EFFECT = 2

    /**
     * 播放音效
     */
    fun effect(soundId: Int) {
        soundPool.play(soundId, 1F, 1F, 0, 0, 1F)
    }

    fun effectByResId(resId: Int) {
        when (resId) {
            R.raw.n0 -> effect(3)
            R.raw.n1 -> effect(4)
            R.raw.n2 -> effect(5)
            R.raw.n3 -> effect(6)
            R.raw.n4 -> effect(7)
            R.raw.n5 -> effect(8)
            R.raw.n6 -> effect(9)
            R.raw.n7 -> effect(10)
            R.raw.n8 -> effect(11)
            R.raw.n9 -> effect(12)
            R.raw.n10 -> effect(13)
            R.raw.n100 -> effect(14)
            R.raw.n1000 -> effect(15)
            R.raw.n10000 -> effect(16)
            R.raw.ndot -> effect(17)
            R.raw.point -> effect(18)
            R.raw.kg -> effect(19)
            R.raw.unit_of_bottle -> effect(20)
        }
    }

    fun releaseSoundPool() {
        soundPool.release()
    }

}